﻿using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ITSP.SharePoint.CacheRefresh.Common;
using Microsoft.Practices.SharePoint.Common.Configuration;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace ITSP.SharePoint.CacheRefresh
{
    /// <summary>
    /// A Base Configuration Manager that uses a SharePoint List to store its configuration.
    /// </summary>
    /// <createdby>Simon Doy</createdby>
    public abstract class BaseSPListConfigurationManager : BaseConfigurationManager, IDisposable
    {
        #region Private Members

        private SPList _configurationList;
        private bool _disposeOfWeb = false;

        #endregion
        
        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="configurationListUrl">The Url of the SharePoint List that will be loaded to host the configuration elements.</param>
        protected BaseSPListConfigurationManager(string configurationListUrl)
            :base(ResolveSharePointList(configurationListUrl).ParentWeb.Site.Url)
        {
            _configurationList = ResolveSharePointList(configurationListUrl);
        }

        #endregion
        
        /// <summary>
        /// Resolve the SharePoint List in a web and site.
        /// </summary>
        /// <param name="listUrl">List to SharePoint Url to resolve to.</param>
        /// <returns></returns>
        protected static SPList ResolveSharePointList(string listUrl)
        {
            SPList configurationList = null;

            try
            {
                Uri listUri=new Uri(listUrl);
                using(SPSite site=new SPSite(listUri.ToString()))
                {
                    using(SPWeb web = site.OpenWeb(listUri.LocalPath))
                    {
                        if(!web.Exists)
                        {
                            throw new FileNotFoundException(String.Format("Cannot find SPWeb {0}", listUri.ToString()));
                        }

                        configurationList = web.GetList(listUri.LocalPath);

                    }
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }

            return configurationList;
        }

        /// <summary>
        /// Saves the Hashtable object to the SharePoint List.
        /// This operation will also refresh the cache using the Cache Dependencies.
        /// </summary>
        /// <param name="configuration">The Hashtable that is to be saved to the SharePoint List.</param>
        /// <returns>True/False depending on the success of the Save operation</returns>
        public new bool Save(Hashtable configuration)
        {
            bool bSuccess = false;
            logger.TraceToDeveloper("BaseSPListConfigurationManager::Save() Enter", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            try
            {
                logger.TraceToDeveloper(String.Format("Saving Configuration {0}", configuration.GetType()), 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);

                SPListItemCollection listItemCollection = _configurationList.Items;
                foreach(string configurationKey in configuration.Keys)
                {
                    //add or update
                    SPListItem item = listItemCollection.OfType<SPListItem>().FirstOrDefault(listItem => listItem.Title == configurationKey);
                    if(item==null)
                    {
                        item = listItemCollection.Add();
                        item[Common.Constants.Fields.Title] = configurationKey;
                    }
                    
                    item["Value"] = configuration[configurationKey].ToString();
                    item.Update();
                }

                AddToCache<Hashtable>(configuration);
                bSuccess = true;

                Refresh();

                logger.TraceToDeveloper("Saved Configuration Successfully", 0, TraceSeverity.Monitorable, Constants.Logging.DefaultLoggingCategory);

            }
            catch (Exception ex)
            {
                logger.TraceToDeveloper(ex, 10000, TraceSeverity.High, Constants.Logging.DefaultLoggingCategory);
                Trace.TraceError("BaseSPListConfigurationManager::Save() {0}", ex.Message);
                throw ex;
            }

            logger.TraceToDeveloper("BaseSPConfigurationManager::Save() Exit", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            return bSuccess;
        }

        /// <summary>
        /// Loads in the configuration items from SharePoint List
        /// </summary>
        /// <returns>Returns a Hashtable from the SharePoint List.</returns>
        /// <exception cref="ConfigurationException">Returned if no configuration found.</exception>
        public new Hashtable Load()
        {
            logger.TraceToDeveloper("BaseSPListConfigurationManager::Load() Enter", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);
            Hashtable configuration = null;

            try
            {
                configuration = base.GetFromCache<Hashtable>();
                if (configuration == null)
                {
                    try
                    {
                        
                        SPListItemCollection listItemCollection = _configurationList.Items;
                        configuration = new Hashtable(listItemCollection.Count);

                        System.Data.DataTable listItemsDataTable = listItemCollection.GetDataTable();
                        if(!listItemsDataTable.Columns.Contains("Title") || !listItemsDataTable.Columns.Contains("Value"))
                        {
                            throw new DataException("SharePoint List does not have expected fields of Title and Value");
                        }

                        foreach (System.Data.DataRow dataRow in listItemsDataTable.Rows)
                        {
                            string title = dataRow["Title"].ToString();
                            string value = dataRow["Value"].ToString();
                            configuration.Add(title,value);
                        }

                        //Add the item to the cache!
                        AddToCache<Hashtable>(configuration);
                    }
                    catch (Exception ex)
                    {
                        logger.TraceToDeveloper(ex, "BaseSPListConfigurationManager::Load() Exception", 10000, TraceSeverity.Medium, Constants.Logging.DefaultLoggingCategory);
                    }
                }
            }
            catch (ConfigurationException ex)
            {
                logger.TraceToDeveloper(ex, 10000, TraceSeverity.Medium, Constants.Logging.DefaultLoggingCategory);
                throw ex;
            }

            logger.TraceToDeveloper("BaseSPListConfigurationManager::Load() Exit", 0, TraceSeverity.Verbose, Constants.Logging.DefaultLoggingCategory);

            return configuration;
        }

        /// <summary>
        /// Dispose of the object and its internal objects.
        /// </summary>
        public void Dispose()
        {
            if (_disposeOfWeb)
            {
                if (_configurationList != null)
                {
                    _configurationList.ParentWeb.Dispose();
                    _configurationList = null;
                }
            }
        }
    }

}
